package com.yuzhihao.writejvm.method;

import com.yuzhihao.writejvm.attributes.Attributes;
import com.yuzhihao.writejvm.attributes.Code;
import com.yuzhihao.writejvm.attributes.LocalVariableTableAttribute;
import com.yuzhihao.writejvm.clazz.ClassInfo;
import com.yuzhihao.writejvm.code.Operation;
import com.yuzhihao.writejvm.code.StackFrame;
import com.yuzhihao.writejvm.constant.ConstantUtf8Info;
import com.yuzhihao.writejvm.format.AttributesType;
import com.yuzhihao.writejvm.format.ClassFormatEnum;
import com.yuzhihao.writejvm.format.PrintFormat;
import com.yuzhihao.writejvm.read.Parse;
import com.yuzhihao.writejvm.read.TypeFatory;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.log4j.Log4j2;

import java.io.DataInputStream;
import java.io.IOException;
import java.util.*;

/**
 * 方法信息
 *
 * @author yzh
 * @since 2021年12月12日17:52:05
 */
@Data
@Log4j2
@EqualsAndHashCode(callSuper = true)
public class MethodInfo extends Methods {

    private ClassFormatEnum cfe_m = ClassFormatEnum.methods;
    private int access_flags_u2 = 2;
    private int name_index_u2 = 2;
    private int descriptor_index_u2 = 2;
    private int attributes_count_u2 = 2;

    private int access_flags;

    private int name_index;

    private int descriptor_index;

    private int attributes_count;

    private Map<String, Attributes> attributes = new HashMap<>();

    @Override
    public void parse(DataInputStream dis, ClassInfo ci) {
        try {
            this.access_flags = Parse.byteToInt(this.access_flags_u2, dis);
            this.name_index = Parse.byteToInt(this.name_index_u2, dis);
            this.descriptor_index = Parse.byteToInt(this.descriptor_index_u2, dis);
            this.attributes_count = Parse.byteToInt(this.attributes_count_u2, dis);
        } catch (IOException e) {
            log.error(e);
        }

        this.out(dis, ci);
    }

    public void out(DataInputStream dis, ClassInfo ci) {
        super.setCi(ci);
        if (this.attributes_count > 0) {
            Attributes a = new Attributes(this.attributes_count);
            a.parse(dis, ci);
            for (Attributes attribute : a.getAttributes()) {
                this.attributes.put(attribute.getAttribute_name(), attribute);
            }
        }
    }

    public String print(ClassInfo ci) {
        StringBuilder builder = new StringBuilder();
        builder.append(PrintFormat.EMPTY);
        List<TypeFatory.Access> accesses = TypeFatory.access_flags(this.access_flags);
        for (TypeFatory.Access access : accesses) {
            builder.append(access.getName()).append(PrintFormat.EMPTY);
        }
        ConstantUtf8Info ni = (ConstantUtf8Info) ci.getCp_info().getPools().get(String.valueOf(this.name_index));
        ConstantUtf8Info di = (ConstantUtf8Info) ci.getCp_info().getPools().get(String.valueOf(this.descriptor_index));
        //描述符
        builder.append(TypeFatory.field_type(TypeFatory.parse_desc(di.getConstant()))).append(PrintFormat.EMPTY);

        if ("<init>".equals(ni.getConstant())) {
            builder.append(super.ci.getThis_class().getClazz());
        } else {
            builder.append(ni.getConstant());
        }
        builder.append("(");
        builder.append(TypeFatory.parse_parameters(di.getConstant()));
        builder.append(")")
                .append(PrintFormat.SEMICOLON)
                .append(System.lineSeparator());
        builder.append(PrintFormat.MEMPTY)
                .append("descriptor")
                .append(PrintFormat.COLON)
                .append(di.getConstant())
                .append(System.lineSeparator());
        builder.append(PrintFormat.MEMPTY).append("flags").append(PrintFormat.COLON);
        for (TypeFatory.Access access : accesses) {
            builder.append(access.getFname()).append(",");
        }
        builder.delete(builder.length() - 1, builder.length());
        builder.append(System.lineSeparator());
        for (Attributes attribute : this.attributes.values()) {
            builder.append(attribute.print(PrintFormat.MEMPTY));
        }
        builder.append(System.lineSeparator());

        return builder.toString();
    }

    public void exec(StackFrame sf) {
        StackFrame myself = new StackFrame();
        myself.setPre(sf);

        //处理本地变量表
        Code mcode = (Code)this.attributes.get(AttributesType.Code);
        LocalVariableTableAttribute localVariableTableAttribute = (LocalVariableTableAttribute)mcode.getCattributes().get(AttributesType.LocalVariableTable);
        myself.setLvs(new Object[localVariableTableAttribute.getLocal_variable_table_length()]);

        //存入指令
        for (Operation operation : mcode.getOperations()) {
            myself.getInstructions().put(operation.getIndex(),operation);
            myself.setMaxpc(operation.getIndex());
        }

        //执行指令
        Operation next = myself.next();
        while (Objects.nonNull(next)){
            next.exec(myself);
            next = myself.next();
        }
    }

}
